package com.nulldev.util.web.nTubeExtensions.yte_for_archives;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.nulldev.util.JVM.JVM;
import com.nulldev.util.VariableAPI.StringsUtil;
import com.nulldev.util.internal.backport.concurrency9.Lists;
import com.nulldev.util.languages.json.uniapi.UniJsonValue;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.web.YouTubeExtractor.formats.Formats;
import com.nulldev.util.web.legacyURI.LegacyURIUtils;

interface YTEArchiveExtractorImpl {
	static final String PARAMETER_SEPARATOR = "&";
	static final String NAME_VALUE_SEPARATOR = "=";
	static final String BASEURL = "https://www.youtube.com/";
	static final String BASEURL_NEL = "https://www.youtube.com";
	static final String WATCHV = "https://www.youtube.com/watch?v=%s";
	static final String JSPLAYER = "ytplayer\\.config\\s*=\\s*([^\\n]+);";
	static final String FUNCCALL = "([$\\w]+)=([$\\w]+)\\(((?:\\w+,?)+)\\)$";
	static final String OBJCALL = "([$\\w]+).([$\\w]+)\\(((?:\\w+,?)+)\\)$";
	static final String[] REGEX_PRE =
		{ "*", ".", "?", "+", "$", "^", "[", "]", "(", ")", "{", "}", "|", "\\", "/" };

	static List<FmtStreamMap> call(final String pageContent) {
		try {
			final Pattern jsPattern = Pattern.compile(JSPLAYER, Pattern.MULTILINE);
			final Matcher matcher = jsPattern.matcher(pageContent);
			if (matcher.find()) {
				final UniJsonValue ytplayerConfig = UniJsonValue.parse(matcher.group(1));
				final UniJsonValue args = ytplayerConfig.get("args");

				String html5playerJS = ytplayerConfig.get("assets").getString("js", "N/A");
				if (html5playerJS.startsWith("//")) {
					html5playerJS = "http:" + html5playerJS;
				} else if (html5playerJS.startsWith("/")) {
					html5playerJS = BASEURL_NEL + html5playerJS;
				} else {
					LoggerFactory.getLogger().warn("call(pgc) -> html5PlayerJS is not a link!");
				}

				final String fmtStream = args.getString("url_encoded_fmt_stream_map", "N/A");

				final String[] fmtArray = fmtStream.split(",");

				final List<FmtStreamMap> streamMaps = new ArrayList<FmtStreamMap>();
				for (final String fmt : fmtArray) {
					final FmtStreamMap parseFmtStreamMap = parseFmtStreamMap(new Scanner(fmt), "utf-8");
					parseFmtStreamMap.html5playerJS = html5playerJS;
					parseFmtStreamMap.videoid = args.getString("video_id", "N/A");
					parseFmtStreamMap.title = args.getString("title", "N/A");
					streamMaps.add(parseFmtStreamMap);
				}

				final String adaptiveStream = args.getString("adaptive_fmts", "N/A");
				final String[] adaptiveStreamArray = adaptiveStream.split(",");

				for (final String fmt : adaptiveStreamArray) {
					final FmtStreamMap parseFmtStreamMap = parseFmtStreamMap(new Scanner(fmt), "utf-8");
					parseFmtStreamMap.html5playerJS = html5playerJS;
					parseFmtStreamMap.videoid = args.getString("video_id", "N/A");
					parseFmtStreamMap.title = args.getString("title", "N/A");
					streamMaps.add(parseFmtStreamMap);
				}

				return streamMaps;
			} else {
				return Lists.of();
			}
		} catch (Exception ex) {
			JVM.errorln(ex);
			return Lists.of();
		}
	}

	static FmtStreamMap parseFmtStreamMap(final Scanner scanner, final String encoding) {
		final FmtStreamMap streamMap = new FmtStreamMap();
		scanner.useDelimiter(PARAMETER_SEPARATOR);
		while (scanner.hasNext()) {
			final String[] nameValue = scanner.next().split(NAME_VALUE_SEPARATOR);
			if (nameValue.length == 0 || nameValue.length > 2) {
				throw new IllegalArgumentException("bad parameter");
			}

			final String name = LegacyURIUtils.safeDecode(nameValue[0], encoding);
			String value = null;
			if (nameValue.length == 2) {
				value = LegacyURIUtils.safeDecode(nameValue[1], encoding);
			}

			if (StringsUtil.equals("fallback_host", name)) {
				streamMap.fallbackHost = value;
			}
			if (StringsUtil.equals("s", name)) {
				streamMap.s = value;
			}
			if (StringsUtil.equals("itag", name)) {
				streamMap.itag = value;
			}
			if (StringsUtil.equals("type", name)) {
				streamMap.type = value;
			}
			if (StringsUtil.equals("quality", name)) {
				streamMap.quality = value;
			}
			if (StringsUtil.equals("url", name)) {
				streamMap.url = value;
			}
			if (StringsUtil.equals("sig", name)) {
				streamMap.sig = value;
			}
			if (!StringsUtil.isBlank(streamMap.itag)) {
				streamMap.format = Formats.get(Integer.parseInt(streamMap.itag));
			}

		}
		return streamMap;
	}
}
